package bootstrap

import (
	"context"
	"fmt"
	"ia/apps/admin/assets/static"
	oout "ia/apps/admin/dto/oout/admin"
	"ia/apps/admin/middleware"
	"ia/apps/admin/middleware/logsink"
	"ia/common/support"
	"ia/common/support/global"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/arl/statsviz"
	"github.com/go-playground/validator/v10"
	"github.com/iris-contrib/middleware/cors"
	"github.com/kataras/golog"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/middleware/rate"
	"github.com/kataras/iris/v12/middleware/recover"
)

type Configurator func(*Bootstrapper)

type Bootstrapper struct {
	*iris.Application
	AppName  string
	AppOwner string
	Api      iris.Party
}

func New(appName, appOwner string, cfgs ...Configurator) *Bootstrapper {
	b := &Bootstrapper{
		Application: iris.New(),
		AppName:     appName,
		AppOwner:    appOwner,
	}
	b.Configure(cfgs...)
	return b
}

func (b *Bootstrapper) Configure(cfgs ...Configurator) {
	for _, cfg := range cfgs {
		cfg(b)
	}
}

/*
 * 缓存所有路由信息
 */
func (b *Bootstrapper) BuildIfaceList() {
	list := make([]*oout.AdminRoutesOut, 0)
	for _, v := range b.GetRoutes() {
		split := strings.Split(v.Title, "~")
		if len(split) == 3 {
			if v.Method == "GET" || v.Method == "POST" || v.Method == "DELETE" || v.Method == "PUT" {
				list = append(list, &oout.AdminRoutesOut{
					Path:      v.Path,
					Method:    v.Method,
					Suf:       ".*",
					Tenant:    global.GConfig.App.Own.Tenant,
					GroupName: split[0],
					Name:      split[1],
					Memo:      split[2],
				})
			}
		}
	}
	oout.GIfaceList = list
	// andtv的select group用
	/*
		counter := 1
		i := 0
		var j int
		for {
			if i >= len(list) {
				break
			}
			for j = i + 1; j < len(list) && list[i].GroupName == list[j].GroupName; j++ {
			}

			options := make([]any, 0)
			for _, v := range list[i:j] {
				options = append(options, iris.Map{
					"key":   counter,
					"value": v.Path,
					"label": v.Name,
				})
				counter++
			}
			pout.GIfaceList = append(pout.GIfaceList, iris.Map{
				"key":     counter,
				"label":   list[i].GroupName,
				"options": options,
			})
			i = j
			counter++
		}
	*/
}

func (b *Bootstrapper) setupErrorHandlers() {
	b.OnAnyErrorCode(func(ctx iris.Context) {
		var code global.Code
		switch ctx.GetStatusCode() {
		case iris.StatusNotFound:
			code = global.CodeNotFount
		default:
			code = global.CodeFailure
		}
		support.Error_(ctx, ctx.GetStatusCode(), code, "服务器错误：%v", ctx.GetErr())
	})
}

// localhost:10087/statsviz/
func (b *Bootstrapper) setupMonitor() {
	statsvizPath := "/statsviz/"
	serveRoot := statsviz.IndexAtRoot(statsvizPath)
	serveWS := statsviz.NewWsHandler(time.Second * 6)
	b.UseRouter(func(ctx iris.Context) {
		if strings.HasPrefix(ctx.Path(), statsvizPath+"ws") {
			serveWS(ctx.ResponseWriter(), ctx.Request())
		} else if strings.HasPrefix(ctx.Path(), statsvizPath) {
			serveRoot(ctx.ResponseWriter(), ctx.Request())
		} else {
			ctx.Next()
		}
	})
}

func (b *Bootstrapper) setupCros() {
	crs := cors.New(cors.Options{
		AllowedOrigins: []string{"*"},
		AllowedHeaders: []string{"*"},
		AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		//Debug:            true,
	})
	b.UseRouter(crs)
}

func (b *Bootstrapper) setupGlobal() {
	b.SetExecutionRules(iris.ExecutionRules{
		Done: iris.ExecutionOptions{Force: true},
	})
	b.UseGlobal(func(ctx iris.Context) {
		ctx.Values().Set("st", time.Now().UnixMicro())
		ctx.Next()
	})
	b.DoneGlobal(done)

	iris.RegisterOnInterrupt(logsink.GLogSink.Flush)
}

func (b *Bootstrapper) setupApi() {
	b.Validator = validator.New()
	// 每秒可以1个请求,最大并发6. 每分钟检查,超过5分钟的移除掉
	limit := rate.Limit(1, 6, rate.PurgeEvery(time.Minute, 5*time.Minute))
	api := b.Party("/api/admin")
	api.Use(limit)
	api.Use(middleware.ServeHTTP)
	b.Api = api
}

// Bootstrap prepares our application.
//
// Returns itself.
func (b *Bootstrapper) Bootstrap() *Bootstrapper {
	b.HandleDir("/", static.AssetFile())
	b.HandleDir("/static", static.AssetFile())

	b.setupErrorHandlers()
	b.setupMonitor()
	b.setupGlobal()
	b.setupCros()

	// middleware, after static files
	b.Use(recover.New())
	b.setupApi()

	return b
}

// Listen starts the http server with the specified "addr".
func (b *Bootstrapper) Listen() {
	idleConnsClosed := make(chan struct{})
	go func() {
		ch := make(chan os.Signal, 1)
		signal.Notify(ch,
			os.Interrupt,   // kill -SIGINT XXXX or Ctrl+c
			syscall.SIGINT, // register that too, it should be ok
			syscall.SIGQUIT,
			// os.Kill,         // is equivalent with the syscall.Kill
			// syscall.SIGKILL, // register that too, it should be ok
			syscall.SIGTERM, // kill -SIGTERM XXXX
		)
		select {
		case aa := <-ch:
			fmt.Printf("shutdown... %v", aa)
			golog.Errorf("关闭了，曹")

			timeout := 3 * time.Second
			ctx, cancel := context.WithTimeout(context.Background(), timeout)
			defer cancel()
			b.Shutdown(ctx)
			close(idleConnsClosed)
		default:
		}
	}()

	// if err := app.Listen(fmt.Sprintf("0.0.0.0:%d", global.GConfig.App.Own.Port),
	// 	iris.WithoutInterruptHandler,
	// 	iris.WithConfiguration(global.GConfig.App.Configuration)); err != nil {
	// 	golog.Fatalf("%s启动失败,原因:%v", b.AppName, err)
	// }

	if err := b.Run(
		iris.Addr(fmt.Sprintf("0.0.0.0:%d", global.GConfig.App.Own.Port)),
		iris.WithoutInterruptHandler,
		iris.WithConfiguration(global.GConfig.App.Configuration),
	); err != nil {
		golog.Fatalf("%s启动失败,原因:%v", b.AppName, err)
	}
	<-idleConnsClosed
}
